#include "helper/sqlite/Statement.h"

#include <sqlite3.h>
#include <cassert>

#include "helper/sqlite/IDB.h"
#include "helper/sqlite/Error.h"

namespace helper {
namespace sqlite {

Statement::Statement(const IDB * db, void * stmt)
: _db(db)
, _stmt(stmt)
{
}
Statement::~Statement(){
  //only construtor in create , after moved free _stmt once
  if(_stmt != nullptr){ //test is important for move_syntax
    std::error_code && ec = error::sqlite_error(sqlite3_finalize(static_cast<sqlite3_stmt*>(_stmt)));
    if(ec){
      error::ShowErrorMsg(__func__, __LINE__, ec);
    }
    assert(!ec);
    _stmt = nullptr;
  }
  if(_db != nullptr) { // test is important for move_syntax
    _db = nullptr;
  }
}


// clear the other.handler_ptr, NOTE: free the resource forbidden
void Statement::__moveImpl(Statement & other){
  //move other to this
  _db = other._db;
  _stmt = other._stmt;

  //clear other
  other._stmt = nullptr;
  other._db = nullptr;
}

Statement::Statement(Statement && other)
: _db(other._db)
,_stmt(other._stmt)
{
  __moveImpl(other);
}

Statement & Statement::operator= (Statement && other){
  __moveImpl(other);
  return *this;
}



std::error_code Statement::reset(){
  if(_stmt != nullptr){
    std::error_code && ec = error::sqlite_error(sqlite3_reset(static_cast<sqlite3_stmt*>(_stmt)));
    if(ec){
      error::ShowErrorMsg(__func__, __LINE__, ec);
    }
    return ec;
  }
  return std::make_error_code(std::errc::invalid_argument);
}



//note lifetime of text: alive in executing
std::error_code Statement::textBind(int index, const char * buf, int len) {
  if(_stmt != nullptr){
    return error::sqlite_error(sqlite3_bind_text(static_cast<sqlite3_stmt*>(_stmt), index, buf, len, SQLITE_STATIC));
  }
  return std::make_error_code(std::errc::invalid_argument);
}
//note lifetime of text: alive in executing
std::error_code Statement::textBind(int index, const std::string & text){
  if(_stmt != nullptr){
    return error::sqlite_error(sqlite3_bind_text(static_cast<sqlite3_stmt*>(_stmt), index, text.c_str(), text.size(), SQLITE_STATIC));
  }
  return std::make_error_code(std::errc::invalid_argument);
}

//note lifetime of binary: alive in executing
std::error_code Statement::binaryBind(int index, const void * binary, int len){
  if(_stmt != nullptr){
    return error::sqlite_error(sqlite3_bind_blob(static_cast<sqlite3_stmt*>(_stmt), index, binary, len, SQLITE_STATIC));
  }
  return std::make_error_code(std::errc::invalid_argument);
}
//note lifetime of binary: alive in executing
std::error_code Statement::binaryBind(int index, const std::string & binary){
  if(_stmt != nullptr){
    return error::sqlite_error(sqlite3_bind_blob(static_cast<sqlite3_stmt*>(_stmt), index, binary.c_str(), binary.size(), SQLITE_STATIC));
  }
  return std::make_error_code(std::errc::invalid_argument);
}





//note lifetime of binary: alive in executing
std::error_code Statement::binary64Bind(int index, const void * binary, int64_t len){
  if(_stmt != nullptr){
    return error::sqlite_error(sqlite3_bind_blob64(static_cast<sqlite3_stmt*>(_stmt), index, binary, len, SQLITE_STATIC));
  }
  return std::make_error_code(std::errc::invalid_argument);
}
//note lifetime of binary: alive in executing
std::error_code Statement::binary64Bind(int index, const std::string & binary){
  if(_stmt != nullptr){
    return error::sqlite_error(sqlite3_bind_blob64(static_cast<sqlite3_stmt*>(_stmt), index, binary.c_str(), binary.size(), SQLITE_STATIC));
  }
  return std::make_error_code(std::errc::invalid_argument);
}


std::error_code Statement::intBind(int index, const int value){
  if(_stmt != nullptr){
    return error::sqlite_error(sqlite3_bind_int(static_cast<sqlite3_stmt*>(_stmt), index, value));
  }
  return std::make_error_code(std::errc::invalid_argument);
}
std::error_code Statement::int64Bind(int index, const int64_t value){
  if(_stmt != nullptr){
    return error::sqlite_error(sqlite3_bind_int64(static_cast<sqlite3_stmt*>(_stmt), index, value));
  }
  return std::make_error_code(std::errc::invalid_argument);
}

std::error_code Statement::doubleBind(int index, const double value){
  if(_stmt != nullptr){
    return error::sqlite_error(sqlite3_bind_double(static_cast<sqlite3_stmt*>(_stmt), index, value));
  }
  return std::make_error_code(std::errc::invalid_argument);
}


std::error_code Statement::nullBind(int index) {
  if(_stmt != nullptr){
    return error::sqlite_error(sqlite3_bind_null(static_cast<sqlite3_stmt*>(_stmt), index));
  }
  return std::make_error_code(std::errc::invalid_argument);
}

std::error_code Statement::zeroBinaryBind(int index, int len){
  if(_stmt != nullptr){
    return error::sqlite_error(sqlite3_bind_zeroblob(static_cast<sqlite3_stmt*>(_stmt), index, len));
  }
  return std::make_error_code(std::errc::invalid_argument);
}
std::error_code Statement::zeroBinary64Bind(int index, int64_t len){
  if(_stmt != nullptr){
    return error::sqlite_error(sqlite3_bind_zeroblob64(static_cast<sqlite3_stmt*>(_stmt), index, len));
  }
  return std::make_error_code(std::errc::invalid_argument);
}


std::error_code Statement::clearBindings(){
  if(_stmt != nullptr) {
    return error::sqlite_error(sqlite3_clear_bindings(static_cast<sqlite3_stmt*>(_stmt)));
  }
  return std::make_error_code(std::errc::invalid_argument);
}

std::error_code Statement::step(){
  return error::sqlite_error(sqlite3_step(static_cast<sqlite3_stmt*>(_stmt)));
}





ActionResult Statement::action(){
  if(_stmt != nullptr){
    return ActionResult(step());
  }
  return ActionResult(std::make_error_code(std::errc::invalid_argument));
}

ExecutionResult Statement::execute(){
  if(_stmt != nullptr && _db != nullptr){
    return ExecutionResult(step(), _db);
  }
  return ExecutionResult::make_error(std::errc::invalid_argument);
}

QueryResult Statement::query() const{
  if(_stmt != nullptr && _db != nullptr){
    return QueryResult(const_cast<Statement*>(this)->step(), _stmt);
  }
  return QueryResult::make_error(std::errc::invalid_argument);
}

//TODO: next version to think more & refactoring 
QueryResult Statement::queryNoNext() const{
  if(_stmt != nullptr && _db != nullptr){
    return QueryResult(std::error_code(), _stmt);
  }
  return QueryResult::make_error(std::errc::invalid_argument);
}


}//namespace sqlite
}//namespace helper
